package com.cly.utils;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.cly.enums.FileCategoryEnum;
import com.cly.properties.FileProperties;
import com.cly.result.exception.ServiceException;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * 文件上传工具类
 *
 * @author by 春风能解释
 * <p>
 * 2025/3/1
 */
public class FileUtils {

    /**
     * 文件类型映射
     */
    private final static Map<String, FileCategoryEnum> FILE_MAPPING = MapUtil
            .builder(new HashMap<String, FileCategoryEnum>())
            .put("jpg", FileCategoryEnum.IMAGE)
            .put("png", FileCategoryEnum.IMAGE)
            .put("gif", FileCategoryEnum.IMAGE)
            .put("jpeg", FileCategoryEnum.IMAGE)
            .put("bmp", FileCategoryEnum.IMAGE)
            .put("mp4", FileCategoryEnum.VIDEO)
            .put("mp3", FileCategoryEnum.AUDIO)
            .put("ogg", FileCategoryEnum.AUDIO)
            .put("m4a", FileCategoryEnum.AUDIO)
            .put("aac", FileCategoryEnum.AUDIO)
            .put("mpeg", FileCategoryEnum.VIDEO)
            .put("avi", FileCategoryEnum.VIDEO)
            .put("wmv", FileCategoryEnum.VIDEO)
            .put("wav", FileCategoryEnum.AUDIO)
            .put("mpg", FileCategoryEnum.VIDEO)
            .put("mkv", FileCategoryEnum.VIDEO)
            .put("flv", FileCategoryEnum.VIDEO)
            .put("mov", FileCategoryEnum.VIDEO)
            .put("wma", FileCategoryEnum.AUDIO)
            .put("txt", FileCategoryEnum.TEXT)
            .put("doc", FileCategoryEnum.DOCUMENT)
            .put("docx", FileCategoryEnum.DOCUMENT)
            .put("xls", FileCategoryEnum.DOCUMENT)
            .put("xlsx", FileCategoryEnum.DOCUMENT)
            .put("pdf", FileCategoryEnum.DOCUMENT)
            .put("ppt", FileCategoryEnum.DOCUMENT)
            .put("pptx", FileCategoryEnum.DOCUMENT)
            .put("zip", FileCategoryEnum.OTHER)
            .put("tar", FileCategoryEnum.OTHER)
            .build();
    /**
     * 文件存储路径
     */
    private static final FileProperties fileProperties = SpringUtil.getBean(FileProperties.class);

    /**
     * 路径分割符
     */
    private static final char SPECIAL_CASE = File.separatorChar;

    /**
     * 获取文件类型
     *
     * @param fileName 文件名
     * @return 文件类型
     */
    public static String getFileType(String fileName) {
        if (!StringUtils.hasText(fileName)) {
            return null;
        }
        int index = fileName.lastIndexOf(".");
        if (index == -1) {
            return "txt";
        }
        return fileName.substring(index + 1);
    }

    /**
     * 文件存储
     *
     * @param file 文件
     * @param id   ID
     * @return 本地存储路径
     */
    public static String fileStore(MultipartFile file, String id) {
        return fileStore(file, null, id);
    }

    /**
     * 文件存储
     *
     * @param file 文件
     * @param path 文件存储路径
     * @param id   文件id
     * @return 本地存储路径
     */
    public static String fileStore(MultipartFile file, String path, String id) {
        // 获取根目录
        String rootPath = getRootPath();
        String children;
        try {
            if (Objects.isNull(file)) {
                return null;
            }
            // 获取文件名，截取最后一部分
            //C:/Test/xxx/xxx.jpg -> xxx.jpg
            String fileName = getFileName(file.getOriginalFilename());
            // 判断文件名是否符合规范
            if (!StringUtils.hasText(fileName)) {
                return null;
            }
            // 时间分类 + 文件id + 文件后缀
            //eg: /2023/03/01/123456789/123456789.jpg
            children = StringUtils.hasText(path) ?
                    getTempPath(id, fileName) :
                    getTimeCategory(id, fileName);
            file.transferTo(Path.of(rootPath + children));
        } catch (Exception e) {
            throw new ServiceException(e);
        }
        return children;
    }

    /**
     * 获取文件根存储路径
     *
     * @return 根目录
     */
    public static String getRootPath() {
        String path = fileProperties.getFileStorePath();
        char charAt = path.charAt(path.length() - 1);
        if (charAt != SPECIAL_CASE) {
            path += SPECIAL_CASE;
        }
        ifNotExistsCreate(path);
        return path;
    }

    /**
     * 获取时间分类路径
     *
     * @param id       文件id
     * @param fileName 文件名
     * @return 时间分类路径
     */
    public static String getTimeCategory(String id, String fileName) {
        String type = getFileType(fileName);
        String children = timeCategory() // 时间分类
                + type
                + SPECIAL_CASE;
        ifNotExistsCreate(getRootPath() + children);
        return children
                + id // 文件id
                + "."
                + type;
    }

    /**
     * 获取临时文件路径
     *
     * @param id       文件id
     * @param fileName 文件名
     * @return 临时文件路径
     */
    public static String getTempPath(String id, String fileName) {
        String type = getFileType(fileName);
        String parent = tempFile() + SPECIAL_CASE + type + SPECIAL_CASE;
        ifNotExistsCreate(getRootPath() + parent);
        //若ID为空则自动生成
        if (!StringUtils.hasText(id)) {
            id = String.valueOf(IdWorker.getId());
        }
        return parent + id + "." + type;
    }

    /**
     * 获取文件名
     *
     * @param originalFilename 源路径
     * @return 文件名
     */
    public static String getFileName(String originalFilename) {
        if (!StringUtils.hasText(originalFilename)) {
            return null;
        }
        originalFilename = originalFilename.replaceAll("\\\\", String.valueOf(SPECIAL_CASE));
        String[] split = originalFilename.split(String.valueOf(SPECIAL_CASE));
        return split[split.length - 1];
    }

    /**
     * 删除文件
     *
     * @param path 文件路径
     * @return 若文件存在且成功删除则返回true，否则返回false
     */
    public static boolean deleteFile(String path) {
        try {
            return Files.deleteIfExists(Path.of(getRootPath() + path));
        } catch (IOException e) {
            throw new ServiceException(e);
        }
    }

    /**
     * 根据后缀名获取文件类型
     *
     * @param suffix 文件后缀名
     * @return 文件类型
     */
    public static Integer getType(String suffix) {
        FileCategoryEnum categoryEnum = FILE_MAPPING.get(suffix);
        return Objects.isNull(categoryEnum) ? FileCategoryEnum.OTHER.getValue() : categoryEnum.getValue();
    }

    /**
     * 拼接时间路径
     *
     * @return 处理后的路径
     */
    public static String timeCategory() {
        String timePath = LocalDateTimeUtil.format(LocalDateTime.now(), SPECIAL_CASE + "yyyy" + SPECIAL_CASE + "MM" + SPECIAL_CASE + "dd");
        ifNotExistsCreate(getRootPath() + timePath);
        return timePath;
    }

    /**
     * 判断文件是否存在
     *
     * @param fileName 文件名
     * @return 是否存在
     */
    public static boolean isExists(String fileName) {
        return new File(fileName).exists();
    }

    /**
     * 若不存在路径则创建
     *
     * @param filePath 路径
     */
    public static void ifNotExistsCreate(String filePath) {
        File file = new File(filePath);
        if (!file.exists()) {
            file.mkdirs();
        }
    }

    /**
     * 获取临时文件路径
     *
     * @return temp文件路径
     */
    public static String tempFile() {
        String tempPath = fileProperties.getFileTempPath();
        String newPath = getRootPath() + tempPath;
        ifNotExistsCreate(newPath);
        return tempPath;
    }
}
